package org.heka.imageproc_forfun;

import java.util.List;

import org.opencv.core.Size;
import org.opencv.highgui.VideoCapture;
import org.opencv.highgui.Highgui;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.hardware.Camera;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;

public abstract class SampleCvViewBase extends SurfaceView implements SurfaceHolder.Callback, Runnable {
    private static final String TAG = "Hekafilter::SurfaceView";

    private SurfaceHolder       mHolder;
    private VideoCapture        mCamera;
    private FpsMeter            mFps;
    private Boolean isFrontChanged = false; 
    private Boolean isBackChanged = false; 
    
    public static Bitmap CaptureImage;    
    public static Boolean isFrontFacing = false;
    public static Boolean isFPSviewOn = false;

    public SampleCvViewBase(Context context) {
        super(context);
        mHolder = getHolder();
        mHolder.addCallback(this);
        mFps = new FpsMeter();
        Log.i(TAG, "Instantiated new " + this.getClass());
    }

    public void surfaceChanged(SurfaceHolder _holder, int format, int width, int height) {
        Log.i(TAG, "surfaceCreated");
        synchronized (this) {
            if (mCamera != null && mCamera.isOpened()) {
            	
            	
                Log.i(TAG, "before mCamera.getSupportedPreviewSizes()");
                List<Size> sizes = mCamera.getSupportedPreviewSizes();
                Log.i(TAG, "after mCamera.getSupportedPreviewSizes()");
                int mFrameWidth = width;
                int mFrameHeight = height;
                
                // selecting optimal camera preview size
                {
                    double minDiff = Double.MAX_VALUE;
                    for (Size size : sizes) {
                        if (Math.abs(size.height - height) < minDiff) {
                            mFrameWidth = (int) size.width;
                            mFrameHeight = (int) size.height;
                            minDiff = Math.abs(size.height - height);
                        }
                    }
                }
                
                
                mCamera.set(Highgui.CV_CAP_PROP_FRAME_WIDTH, mFrameWidth);
                mCamera.set(Highgui.CV_CAP_PROP_FRAME_HEIGHT, mFrameHeight);
            }
        }
    }
    
  
    
    public void surfaceCreated(SurfaceHolder holder) {
        Log.i(TAG, "surfaceCreated");
        mCamera = new VideoCapture(Highgui.CV_CAP_ANDROID);      
        
        if (mCamera.isOpened()) {
            (new Thread(this)).start();
        } else {
            mCamera.release();
            mCamera = null;
            Log.e(TAG, "Failed to open native camera");
        }
    }

    public void surfaceDestroyed(SurfaceHolder holder) {
        Log.i(TAG, "surfaceDestroyed");
        if (mCamera != null) {
            synchronized (this) {
                mCamera.release();
                mCamera = null;
            }
        }
    }

    protected abstract Bitmap processFrame(VideoCapture capture);

    public void run() {
        Log.i(TAG, "Starting processing thread");
        mFps.init();

        while (true) {
            Bitmap bmp = null;

            //Using FrontFacing Camera
        	if(isFrontFacing && !isFrontChanged)
        	{
        		try {
					Thread.sleep(200);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
        		mCamera.release();
        		mCamera.open(openFrontFacingCameraGingerbread_idx());
        		isFrontChanged = true;
        		isBackChanged = false;
        	}
            //Using BackFacing Camera
        	else if(!isFrontFacing && !isBackChanged)
        	{
        		try {
					Thread.sleep(200);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
        		mCamera.release();
        		mCamera.open(openBackFacingCameraGingerbread_idx());
        		isFrontChanged = false;
        		isBackChanged = true;
        	}
        	
            synchronized (this) {
                if (mCamera == null)
                    break;

                if (!mCamera.grab()) {
                    Log.e(TAG, "mCamera.grab() failed");
                    break;
                }
                    
                try {
                	if(mCamera!=null)
                	 bmp = processFrame(mCamera);
				} catch (Exception e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}               

                mFps.measure();
            }
            
          

            if (bmp != null) {
            	
            	//rotate0 for FrontFacingCamera
                Matrix mat = new Matrix();
                //mat.postRotate(180);
                mat.setScale(-1, 1);
                Bitmap bmpRotate = Bitmap.createBitmap(bmp, 0, 0, bmp.getWidth(), bmp.getHeight(), mat, true);
                
                Canvas canvas = mHolder.lockCanvas();
                if (canvas != null) {
                    
                	if(isFrontFacing)
                		canvas.drawBitmap(bmpRotate, (canvas.getWidth() - bmpRotate.getWidth())/2, (canvas.getHeight() - bmpRotate.getHeight())/2, null);                	
                	else
                		canvas.drawBitmap(bmp, (canvas.getWidth() - bmp.getWidth()) / 2, (canvas.getHeight() - bmp.getHeight()) / 2, null);
                		
                    //fps invisible
                	if(isFPSviewOn)
                    mFps.draw(canvas, (canvas.getWidth() - bmp.getWidth()) / 2, 0);
                	
                    mHolder.unlockCanvasAndPost(canvas);
                    
                    //static bitmap image - using Activity
                    CaptureImage = bmp.copy(Config.ARGB_8888, true);
                }
                //bmpRotate.recycle();
                //bmp.recycle();
                mat = null;
            }
        }

        Log.i(TAG, "Finishing processing thread");
    }
    
    private int openFrontFacingCameraGingerbread_idx() {
        int cameraCount = 0;
        int frontfacing_camIdx=0;
        Camera.CameraInfo cameraInfo = new Camera.CameraInfo();
        cameraCount = Camera.getNumberOfCameras();
        for ( int camIdx = 0; camIdx < cameraCount; camIdx++ ) {
            Camera.getCameraInfo( camIdx, cameraInfo );
            if ( cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_FRONT  ) {
            	
            	frontfacing_camIdx = camIdx;
                /*try {
                    cam = Camera.open( camIdx );
                } catch (RuntimeException e) {
                    Log.e(TAG, "Camera failed to open: " + e.getLocalizedMessage());
                }*/
            }
        }
        return frontfacing_camIdx;
    }
    private int openBackFacingCameraGingerbread_idx() {
        int cameraCount = 0;
        int backfacing_camIdx=0;
        Camera.CameraInfo cameraInfo = new Camera.CameraInfo();
        cameraCount = Camera.getNumberOfCameras();
        for ( int camIdx = 0; camIdx < cameraCount; camIdx++ ) {
            Camera.getCameraInfo( camIdx, cameraInfo );
            if ( cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_BACK  ) {
            	
            	backfacing_camIdx = camIdx;
             
            }
        }
        return backfacing_camIdx;
    }
}